<template>
    <div ref="carouselRef" class="pe-carousel" :style="{ height }" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
        <Transition name="left">
            <button class="pe-carousel-pre" @click.stop="onPre" v-show="show">
                <i class="pe-icon">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
                        <path fill="currentColor"
                            d="M609.408 149.376 277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0 30.592 30.592 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.592 30.592 0 0 0 0-42.688 29.12 29.12 0 0 0-41.728 0z">
                        </path>
                    </svg>
                </i>
            </button>
        </Transition>
        <Transition name="right">
            <button class="pe-carousel-next" @click.stop="onNext" v-show="show">
                <i class="pe-icon">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
                        <path fill="currentColor"
                            d="M340.864 149.312a30.592 30.592 0 0 0 0 42.752L652.736 512 340.864 831.872a30.592 30.592 0 0 0 0 42.752 29.12 29.12 0 0 0 41.728 0L714.24 534.336a32 32 0 0 0 0-44.672L382.592 149.376a29.12 29.12 0 0 0-41.728 0z">
                        </path>
                    </svg>
                </i>
            </button>
        </Transition>
        <div class="pe-carousel-indicators">
            <div v-for="item in length" class="pe-carousel-indicator"
                :style="{ backgroundColor: item - 1 === activeItem ? '#ffffff' : '' }" @mouseenter="toItem(item, 0)"
                @click="toItem(item, 1)"></div>
        </div>
        <div ref="itemBoxRef" class="pe-carousel-item-box">
            <slot></slot>
        </div>
    </div>
</template>

<script setup lang="ts">
import { onMounted, ref, watch } from "vue"

/**
 * @param height carousel 的高度
 * @param initial-index 初始状态激活的幻灯片的索引，从 0 开始
 * @param trigger 指示器的触发方式
 * @param autoplay 是否自动切换
 * @param interval 自动切换的时间间隔，单位为毫秒
 * @param arrow 切换箭头的显示时机
 * @param pause-on-hover 鼠标悬浮时暂停自动切换
 */
interface Props {
    height?: string,
    initialIndex?: number,
    trigger?: 'hover' | 'click',
    autoplay?: boolean,
    interval?: number,
    arrow?: 'always' | 'hover' | 'never',
    pauseOnHover?: boolean
}

const props = withDefaults(defineProps<Props>(), {
    initialIndex: 0,
    trigger: "hover",
    autoplay: true,
    interval: 3000,
    arrow: "hover",
    pauseOnHover: true
})

const carouselRef = ref<HTMLDivElement>()
const itemBoxRef = ref<HTMLDivElement>()

let itemHtmlList: NodeListOf<HTMLDivElement>
let width: number
const length = ref<number>(0)
let timer: any = null

const getTransform = (num: number) => `translateX(${num}px)`

onMounted(() => {
    const dom = itemBoxRef.value
    // 选择要观察的属性 
    const config = { childList: true };
    width = carouselRef.value!.offsetWidth
    // 当dom元素发生变化时执行的回调函数
    const callback = () => {
        itemHtmlList = dom!.querySelectorAll<HTMLDivElement>(".pe-carousel-item")
        length.value = itemHtmlList.length
        itemHtmlList?.forEach((item: HTMLDivElement, index: number) => {
            if(index === props.initialIndex) {
                item.style.transform = getTransform(0)
                item.style.zIndex = "9"
            } else if(index > props.initialIndex) {
                item.style.transform = getTransform(width * index)
                item.style.zIndex = "8"
            } else {
                item.style.transform = getTransform(width * (props.initialIndex - index))
                item.style.zIndex = "8"
            }
        })
        activeItem.value = props.initialIndex
    };
    // 创建一个链接到回调函数的观察者实例
    const observer = new MutationObserver(callback);
    // 开始观察变化的目标dom节点
    observer.observe(dom!, config);
})

// 选中的编号
const activeItem = ref<number>(0)

// 按钮显示标志
const show = ref<boolean>(false)

watch(props, newProps => {
    if (newProps.autoplay) {
        timer = setInterval(() => {
            onNext()
        }, newProps.interval)
    } else {
        if (timer) {
            clearInterval(timer)
            timer = null
        }
    }
    show.value = props.arrow === 'always'
}, { immediate: true })

// 上一个
const getPre = (active: number) => {
    let pre: number
    if (active === 0) {
        pre = itemHtmlList.length - 1
    } else {
        pre = active - 1
    }
    return pre
}

const onPre = () => {
    if (itemHtmlList.length > 1) {
        const active: number = activeItem.value
        const pre: number = getPre(active)
        itemHtmlList.forEach((item: HTMLDivElement, index: number) => {
            if (index === pre) {
                item.style.transform = getTransform(0)
                item.style.zIndex = "9"
            } else if(index === getPre(pre)) {
                item.style.transform = getTransform(-width)
                item.style.zIndex = "8"
            } else {
                let transform = item.style.transform
                transform = transform.replace("translateX(", "").replace("px)", "")
                item.style.transform = getTransform(Number(transform) + width)
                item.style.zIndex = "8"
            }
        })
        activeItem.value = pre
    }
}

// 下一个
const getNext = (active: number) => {
    let next: number
    if (active + 1 === itemHtmlList.length) {
        next = 0
    } else {
        next = active + 1
    }
    return next
}

const onNext = () => {
    if (itemHtmlList.length > 1) {
        const active: number = activeItem.value
        const next: number = getNext(active)
        itemHtmlList.forEach((item: HTMLDivElement, index: number) => {
            if (index === next) {
                item.style.transform = getTransform(0)
                item.style.zIndex = "9"
            } else if(index === getNext(next)) {
                item.style.transform = getTransform(width)
                item.style.zIndex = "8"
            } else {
                let transform = item.style.transform
                transform = transform.replace("translateX(", "").replace("px)", "")
                item.style.transform = getTransform(Number(transform) - width)
                item.style.zIndex = "8"
            }
        })
        activeItem.value = next
    }
}

const toItem = (activeIndex: number, flag: number) => {
    if ((props.trigger === 'hover' && flag === 0) || (props.trigger === 'click' && flag === 1)) {
        itemHtmlList.forEach((item: HTMLDivElement, index: number) => {
            if (index === activeIndex - 1) {
                item.style.transform = getTransform(0)
                item.style.zIndex = "9"
            } else if(index > activeIndex - 1) {
                item.style.transform = getTransform(width * index)
                item.style.zIndex = "8"
            } else {
                item.style.transform = getTransform(width * (activeIndex - 1 - index))
                item.style.zIndex = "8"
            }
        })
        activeItem.value = activeIndex - 1
    }
}

// 显示按钮
const onMouseEnter = () => {
    if (props.arrow === 'hover') {
        show.value = true
    }
    if (props.autoplay && props.pauseOnHover) {
        if (timer) {
            clearInterval(timer)
            timer = null
        }
    }
}

// 按钮消失
const onMouseLeave = () => {
    show.value = false
    if (props.autoplay && props.pauseOnHover) {
        timer = setInterval(() => {
            onNext()
        }, props.interval)
    }
}
</script>

<script lang="ts">
export default {
    name: "pe-carousel"
}
</script>
